#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
THN WEB SERVER

Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

This library is dual-licensed.
  + For use in commercial software, please obtain a commercial license from THN Solutions LLC.    

  + For use in free software, this library is licensed under GPL version 3. A summary of 
	GPLv3 is listed below. You should also find a copy of GPLv3 as file License_GPLv3.txt 
	included with the source-code files.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 3 as published by
    the Free Software Foundation  of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#endregion

#region Record of Change
//             							CHANGE HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | DEVELOPER  | DESCRIPTION                                              |
// -------------------------------------------------------------------------------------
// | 14-Nov-12 | HaiNM      | First creation.                                          |
// -------------------------------------------------------------------------------------
//
#endregion

#region Record of Audit
//             							REVIEW HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | REVIEWER   | NOTES                                                    |
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
//
#endregion

#region Using directives
using System;
using System.IO;

using Thn;
using Thn.IO;
using Thn.Net;
using Thn.Net.Http;
using Thn.Collections;
#endregion

namespace Thn.Web
{
    /// <summary>
    /// This handler can return the directory structure to client.
    /// </summary>
#if DEBUG || TESTING
    public
#else
    internal
#endif
 class DirectoryRequestHandler : IHttpRequestHandler
    {
        #region Constants

        #region Style
        const string StyleSection = @"<style>

  h1 {
    border-bottom: 1px solid #c0c0c0;
    margin-bottom: 10px;
    padding-bottom: 10px;
    white-space: nowrap;
  }

  table {
    border-collapse: collapse;
  }

  tr.header {
    font-weight: bold;
  }

  td.detailsColumn {
    padding-left: 2em;
    text-align: right;
    white-space: nowrap;
  }

  a.icon {
    padding-left: 1.5em;
    text-decoration: none;
  }

  a.icon:hover {
    text-decoration: underline;
  }

  a.file {
    background : url(" + @" " + @") left top no-repeat;
  }

  a.dir {
    background : url(" + @" " + @") left top no-repeat;
  }

  a.up {
    background : url(" + @" " + @") left top no-repeat;
  }

  #listingParsingErrorBox {
    border: 1px solid black;
    background: #fae691;
    padding: 10px;
    display: none;
  }
</style>";
        #endregion

        #endregion

        #region Fields
        #endregion

        #region Properties

        #region Path Translator
        private IPathTranslator mPathTranslator;
        /// <summary>
        /// Gets or sets reference to the URI to local path translator.
        /// </summary>
        public IPathTranslator PathTranslator
        {
            get { return mPathTranslator; }
            set { mPathTranslator = value; }
        }
        #endregion

        #region File Service
        private IFileService mFileService;
        /// <summary>
        /// Gets or sets reference to the file handling service.
        /// </summary>
        public IFileService FileService
        {
            get { return mFileService; }
            set { mFileService = value; }
        }
        #endregion

        #region Mime Types
        private IMimeTypes mMimeTypes;
        /// <summary>
        /// Gets or sets reference to MIME types.
        /// </summary>
        public IMimeTypes MimeTypes
        {
            get { return mMimeTypes; }
            set { mMimeTypes = value; }
        }
        #endregion

        #endregion

        #region IHttpRequestHandler Members

        #region Process
        void IHttpRequestHandler.Process(IHttpRequestHandlingContext context)
        {
            string escapedPath = UrlEscape.Decode(context.Request.Path);
            //string path = mPathTranslator.Translate(context.Request.Path);
            //path = UrlEscape.Decode(path);
            string path = mPathTranslator.Translate(escapedPath);
            //string linkPath = context.Request.Path;
            string linkPath = escapedPath;
            string itemName;
            string itemLink;

            if (Directory.Exists(path))
            {
                #region Directory found
                HttpResponse response = new HttpResponse();
                try
                {
                    IContextWriter w = response.Body.Writer;
                    string title = string.Format("Index of {0}", escapedPath);

                    //set MIME type
                    response.ContentType = "text/html";

                    //HTML header
                    w.WriteLine("<!DOCTYPE html>");
                    w.WriteLine("<html>");
                    w.WriteLine("<head>");
                    w.WriteLine("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
                    w.Write(StyleSection, StringMode.Exact);
                    w.WriteLine(string.Format("<title id=\"title\">{0}</title>", title));
                    w.WriteLine("</head>");
                    w.WriteLine("<body>");
                    w.WriteLine("<h1 id=\"header\">" + title + "</h1>");

                    //write table header
                    w.WriteLine("<table id=\"table\">");

                    //write column headers
                    w.WriteLine("<tbody>");
                    w.WriteLine("<tr class=\"header\">");
                    w.WriteLine("<td>Name</td>");
                    w.WriteLine("<td class=\"detailsColumn\">Size</td>");
                    w.WriteLine("<td class=\"detailsColumn\">Type</td>");
                    w.WriteLine("<td class=\"detailsColumn\">Date Modified</td>");
                    w.WriteLine("</tr>");
                    w.WriteLine("</tbody>");

                    #region Write parent link
                    if (linkPath != "/")
                    {
                        w.WriteLine("<tbody>");

                        //row start
                        w.WriteLine("<tr>");
                        //name
                        w.WriteLine("<td>");
                        itemName = "[Parent Folder]";
                        UrlParser parser = new UrlParser();
                        parser.Parse(linkPath);
                        itemLink = "/";
                        for (int i = 0; i < parser.Parts.Count - 1; i++)
                        {
                            itemLink += parser.Parts[i] + "/";
                        }
                        //itemLink = linkPath + "/../";
                        itemLink = UrlEscape.Encode(itemLink);
                        w.Write(string.Format("<a class=\"icon up\" href=\"{0}\">{1}</a", itemLink, itemName), StringMode.Exact);
                        w.WriteLine("</td>");
                        //size
                        w.WriteLine("<td></td>");
                        //type
                        //w.WriteLine("<td align=center>Folder</td>");
                        w.WriteLine("<td class=\"detailsColumn\">Reference</td>");
                        //last modified
                        w.WriteLine("");
                        //row end
                        w.WriteLine("</tr>");

                        w.WriteLine("/<tbody>");
                    }
                    #region Defensive tracing
                    else
                    {
                        //already at root path
                    }
                    #endregion
                    #endregion

                    #region Write children directories
                    string[] children = Directory.GetDirectories(path);
                    List<string> items = new List<string>(children);
                    items.Sort();

                    foreach (var item in items)
                    {
                        DirectoryInfo di = new DirectoryInfo(item);
                        if ((di.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
                        {
                            //row start
                            w.WriteLine("<tr>");
                            //name
                            w.WriteLine("<td>");
                            itemName = Path.GetFileName(item);
                            itemLink = linkPath + itemName + "/";
                            itemLink = UrlEscape.Encode(itemLink);
                            w.Write(string.Format("<a class=\"icon dir\" href=\"{0}\">{1}</a", itemLink, itemName), StringMode.Exact);
                            w.WriteLine("</td>");
                            //size
                            w.WriteLine("<td></td>");
                            //type
                            //w.WriteLine("<td align=center>Folder</td>");
                            w.WriteLine("<td class=\"detailsColumn\">Folder</td>");
                            //last modified
                            //w.WriteLine(string.Format("<td align=center>{0}</td>", di.LastWriteTime));
                            w.WriteLine(string.Format("<td class=\"detailsColumn\">{0}</td>", di.LastWriteTime));
                            //row end
                            w.WriteLine("</tr>");
                        }

                    }
                    #endregion

                    #region Write children files
                    children = Directory.GetFiles(path);
                    items = new List<string>(children);
                    items.Sort();
                    string fileExtension;

                    foreach (var item in items)
                    {
                        FileInfo fi = new FileInfo(item);
                        if ((fi.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
                        {
                            //row start
                            w.WriteLine("<tr>");
                            //name
                            w.WriteLine("<td>");
                            itemName = Path.GetFileName(item);
                            itemLink = linkPath + itemName;
                            itemLink = UrlEscape.Encode(itemLink);
                            w.Write(string.Format("<a class=\"icon file\" href=\"{0}\">{1}</a", itemLink, itemName), StringMode.Exact);
                            w.WriteLine("</td>");
                            //size
                            w.WriteLine(string.Format("<td class=\"detailsColumn\">{0:#,##}</td>", fi.Length));
                            //type
                            fileExtension = Path.GetExtension(item);
                            if (mMimeTypes.ContainsKey(fileExtension))
                            {
                                w.WriteLine(string.Format("<td class=\"detailsColumn\">{0}</td>", mMimeTypes[fileExtension]));
                            }
                            else
                            {
                                w.WriteLine(string.Format("<td class=\"detailsColumn\">{0}</td>", fileExtension));
                            }
                            //last modified
                            w.WriteLine(string.Format("<td class=\"detailsColumn\">{0}</td>", fi.LastWriteTime));
                            //row end
                            w.WriteLine("</tr>");
                        }

                    }
                    #endregion

                    //table footer
                    w.WriteLine("</table>");

                    //HTML footer
                    w.WriteLine("</body>");
                    w.WriteLine("</html>");

                    //return response
                    context.Response = response;
                }
                catch (Exception ex)
                {
                    #region Exception handling

                    response = new HttpResponse();
                    if (ex is UnauthorizedAccessException) context.ResponseStatus = HttpResponseStatus.Unauthorized;
                    else if (ex is FileNotFoundException) context.ResponseStatus = HttpResponseStatus.NotFound;
                    else context.ResponseStatus = HttpResponseStatus.InternalServerError;

                    #endregion
                }

                #endregion
            }
            else
            {
                #region Directory not found -> skip
                #endregion
            }
        }
        #endregion

        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// </summary>
        public DirectoryRequestHandler()
        { }
        #endregion
    }
}
